Imports System.ComponentModel
Imports System.Net
Imports System.IO

Namespace codeconcepts

    Public Class http_downloader
        ' Use the Component so it doesnt appear on the form
        Inherits System.ComponentModel.Component

#Region "Varaibles"

        Private _http_collection_items As New http_collection()
        Private _progressbar As ProgressBar
        Private _Cancel As Boolean = False

#End Region

#Region "Events"

        'Events
        Public Event DownloadFailed(ByVal DownloadURLItem As http_downloaderItem, ByVal DownloadError As String)
        Public Event StartedFileDownload(ByVal DownloadURLItem As http_downloaderItem)
        Public Event EndFileDownload(ByVal DownloadURLItem As http_downloaderItem)
        Public Event BytesReceived(ByVal DownloadURLItem As http_downloaderItem, ByVal Bytes As Integer, ByVal TotalBytes As Integer)
        Public Event DownloadComplete()
        Public Event OnCanel()

#End Region

#Region "Properties"

        Public Property ProgressBar() As ProgressBar
            Get
                Return _progressbar
            End Get
            Set(ByVal Value As ProgressBar)
                _progressbar = Value
            End Set
        End Property
        ' Download list
        <DesignerSerializationVisibility(DesignerSerializationVisibility.Content)> _
        Public ReadOnly Property DownloadURLitems() As http_collection
            Get
                Return _http_collection_items
            End Get
        End Property

#End Region

#Region " Windows Form Designer generated code "

        Public Sub New()
            MyBase.New()

            'This call is required by the Windows Form Designer.
            InitializeComponent()

            'Add any initialization after the InitializeComponent() call

        End Sub

        'UserControl overrides dispose to clean up the component list.
        Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
            If disposing Then
                If Not (components Is Nothing) Then
                    components.Dispose()
                End If
            End If
            MyBase.Dispose(disposing)
        End Sub

        'Required by the Windows Form Designer
        Private components As System.ComponentModel.IContainer

        'NOTE: The following procedure is required by the Windows Form Designer
        'It can be modified using the Windows Form Designer.  
        'Do not modify it using the code editor.
        <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
            components = New System.ComponentModel.Container()
        End Sub

#End Region

#Region "Cancel Sub"

        Public Sub Cancel()
            _Cancel = True
            RaiseEvent OnCanel()
        End Sub

#End Region

#Region "Download routine"

        Public Function Download() As Boolean
            Dim http_downloaderItem As http_downloaderItem
            Dim HTTP_request As HttpWebRequest
            Dim HTTP_response As HttpWebResponse
            Dim Buffer(999) As Byte
            Dim Bytes_Downloaded As Integer
            Dim Bytes_Total As Integer
            Dim ByteCountReceived As Integer = 0
            Dim FileError As Boolean = False
            Dim Anyfailed As Boolean
            For Each http_downloaderItem In _http_collection_items
                Try
                    FileError = False
                    RaiseEvent StartedFileDownload(http_downloaderItem)
                    If Not _progressbar Is Nothing Then _progressbar.Value = 0
                    ' Ask the HTTP server for file
                    HTTP_request = HttpWebRequest.Create(http_downloaderItem.DownloadURL)
                    ' Get the response from the server
                    HTTP_response = HTTP_request.GetResponse
                    ' Make a new file to download into
                    Dim FileStreamer As New FileStream(CreateDirectoryIfNotExist(http_downloaderItem.Folder) & "_" & http_downloaderItem.Filename, FileMode.Create)
                    ' Stream the downloading file
                    Dim IncomingData As Stream = HTTP_request.GetResponse.GetResponseStream
                    ' Set the progress bar Mix
                    If Not _progressbar Is Nothing Then _progressbar.Minimum = 0
                    ' Set the progress bar Max
                    Console.WriteLine(HTTP_response.ContentLength)
                    If Not _progressbar Is Nothing Then
                        If HTTP_response.ContentLength > 0 Then
                            _progressbar.Maximum = HTTP_response.ContentLength
                        Else
                            _progressbar.Maximum = 0
                        End If
                    End If
                    ' Set the total bytes Max
                    Bytes_Total = HTTP_response.ContentLength
                    ' Start of loop
                    Do
                        ' Count the bytes received
                        ByteCountReceived = IncomingData.Read(Buffer, 0, 1000)
                        ' Place the downloaded bytes into file
                        FileStreamer.Write(Buffer, 0, ByteCountReceived)
                        ' Add the bytes Receive to overall total for file
                        Bytes_Downloaded += ByteCountReceived
                        ' Raise event that more bytes have been downloaded
                        RaiseEvent BytesReceived(http_downloaderItem, Bytes_Downloaded, Bytes_Total)
                        ' Inc the value on the progress bar
                        If Not _progressbar Is Nothing Then
                            If _progressbar.Value + ByteCountReceived <= _progressbar.Maximum Then
                                _progressbar.Value += ByteCountReceived
                            Else
                                _progressbar.Value = _progressbar.Maximum
                            End If
                        End If
                        ' Let windows do its thing
                        Application.DoEvents()
                        ' Loop
                    Loop Until ByteCountReceived = 0 Or _Cancel = True
                    ' Reset the bytes recieved counter
                    ByteCountReceived = 0
                    Bytes_Downloaded = 0
                    ' Close the file
                    FileStreamer.Close()
                Catch ex As Exception
                    RaiseEvent DownloadFailed(http_downloaderItem, Err.Description)
                    Application.DoEvents()
                    FileError = True
                    Anyfailed = True
                End Try
                If FileError = False Then
                    Application.DoEvents()
                    'IO.File.Delete(CreateDirectoryIfNotExist(http_downloaderItem.Folder) & http_downloaderItem.Filename)
                    'IO.File.Move(CreateDirectoryIfNotExist(http_downloaderItem.Folder) & "_" & http_downloaderItem.Filename, CreateDirectoryIfNotExist(http_downloaderItem.Folder) & http_downloaderItem.Filename)
                    RaiseEvent EndFileDownload(http_downloaderItem)
                    Application.DoEvents()
                End If
                If _Cancel Then Exit For
            Next
            If _Cancel = True Then
                Throw New Exception("Download cancelled")
                _Cancel = False
                Anyfailed = True
                Exit Function
            End If
            RaiseEvent DownloadComplete()
            If Anyfailed Then Return False Else Return True
        End Function

#End Region

#Region "Directory Creation"

        Private Function CreateDirectoryIfNotExist(ByVal Path As String)
            If Len(Path) > 0 Then
                Dim Folders() As String
                Dim CurrentPath As String
                Dim i As Integer
                If InStr(Path, ":") > 0 Then
                    Folders = Split(Path, "\")
                    ' If it the root directory cancel
                    If UBound(Folders) > 0 Then
                        ' Start path
                        CurrentPath = Folders(0) & "\"
                        For i = 1 To UBound(Folders)
                            If Len(Folders(i)) > 0 Then
                                CurrentPath += Folders(i) & "\"
                                If Not IO.Directory.Exists(CurrentPath) Then
                                    IO.Directory.CreateDirectory(CurrentPath)
                                End If
                            End If
                        Next
                        Return CurrentPath & "\"
                    End If
                Else
                    Folders = Split(Path, "\")
                    ' Start path
                    CurrentPath = Application.StartupPath & "\"
                    For i = 0 To UBound(Folders)
                        If Len(Folders(i)) > 0 Then
                            CurrentPath += Folders(i) & "\"
                            If Not IO.Directory.Exists(CurrentPath) Then
                                IO.Directory.CreateDirectory(CurrentPath)
                            End If
                        End If
                    Next
                    Return CurrentPath & "\"
                End If
            Else
                Return Application.StartupPath & "\"
            End If
        End Function

#End Region

    End Class

    ' The Collection base for HttpDownloadItems
#Region "Http Collection Base"

    Public Class http_collection
        Inherits CollectionBase

        Public Function Add(ByVal Item As http_downloaderItem) As Integer
            Return MyBase.List.Add(Item)
        End Function

        Public Function Add(ByVal DownloadURL As String) As Integer
            Dim Item As New http_downloaderItem()
            Item.DownloadURL = DownloadURL
            Return MyBase.List.Add(Item)
        End Function

        Public Sub Remove(ByVal Item As http_downloaderItem)
            MyBase.List.Remove(Item)
        End Sub

        Public Function IndexOf(ByVal Item As http_downloaderItem) As Integer
            Return MyBase.List.IndexOf(Item)
        End Function

        Default Public Property Items(ByVal Index As Integer) As http_downloaderItem
            Get
                Return CType(MyBase.List.Item(Index), http_downloaderItem)
            End Get
            Set(ByVal Value As http_downloaderItem)
                MyBase.List(Index) = Value
            End Set
        End Property

    End Class

#End Region

    ' Http download items class
#Region "Http Downloader Item"

    <System.ComponentModel.DesignTimeVisibleAttribute(False), System.ComponentModel.DefaultPropertyAttribute("Header"), System.ComponentModel.ToolboxItemAttribute(False)> _
        Public Class http_downloaderItem
        Inherits Component

        Private _UrlToDownload As String
        Private _Filename As String
        Private _Folder As String = Nothing

        Public Property DownloadURL() As String
            Get
                Return _UrlToDownload
            End Get
            Set(ByVal Value As String)
                If (InStr(Value, "http://") > 0) Then
                    Dim Temp_Filename() As String
                    Temp_Filename = Split(Value, "/")
                    _Filename = Temp_Filename(UBound(Temp_Filename))
                    _UrlToDownload = Value
                Else
                    Throw New Exception("Must enter a valid HTTP URL")
                End If
            End Set
        End Property

        Public ReadOnly Property Filename() As String
            Get
                Return _Filename
            End Get
        End Property

        <Category("Folder to save file to blank for application folder"), Description("Folder to save file to blank for application folder")> _
        Public Property Folder() As String
            Get
                Return _Folder
            End Get
            Set(ByVal Value As String)
                _Folder = Value
            End Set
        End Property

    End Class

#End Region

End Namespace
